package conf

import (
	"fmt"
	"gitee.com/lllgo/gopkg/utils"
	"github.com/spf13/viper"
	"gorm.io/gorm/logger"
	"os"
	"strings"
	"time"
)

const (
	AppName        = "opsctl"
	AppVer         = "v0.1.0"
	DefCommandType = Service
	varHome        = "$HOME"
)

var (
	envPrefix       = "OPSCTL_"
	EnvConfig       = envPrefix + "CONFIG"
	EnvInventory    = envPrefix + "INVENTORY"
	EnvPluginConfig = envPrefix + "PLUGIN_CONFIG"

	C Config

	// 仅flag或系统环境变量可设置的配置
	ConfigPath string
	Env        string

	// 插件需要增加到命令行，不能从配置文件读取
	PluginPath string
	//服务器配置文件,需要把配置信息在帮助文档线上，不能从配置文件读取
	InventoryPath string

	// 并行任务间隔时长
	DelayTime time.Duration

	cacheViper   map[string]*viper.Viper
	defViperName = "default"

	cmdTypes map[string]CommandType

	DefInventory string
	DefPlugin    string
	DefCfgFile   string
)

func init() {
	cacheViper = make(map[string]*viper.Viper)
	cacheViper[defViperName] = viper.New()

	cmdTypes = map[string]CommandType{
		"init":      Init,
		"add":       Add,
		"upgrade":   Upgrade,
		"remove":    Remove,
		"start":     Start,
		"stop":      Stop,
		"kit":       Kit,
		"service":   Service,
		"exclusive": Exclusive,
		"todo":      TODO,
	}
	DefInventory, DefCfgFile, DefPlugin = DefaultConfigFile()

	PluginPath = utils.GetDefString(PluginPath, utils.GetEnv(EnvPluginConfig, DefPlugin))

	InventoryPath = utils.GetDefString(InventoryPath, utils.GetEnv(EnvInventory, DefInventory))

}

// opsctl配置文件
type Config struct {
	// 日志文件和日志级别
	LogFile, LogLevel string
	// 数据库文件
	DBDir string
	// 中间文件目录 用于存储下载的资源文件 bindir存储可执行二进制文件 downdir用于存储下载的资源包
	LocalDir  string
	Inventory string
	//并行任务间隔秒数
	DelaySeconds int
}

type PluginConfig struct {
	// 描述信息
	Desc string
	// 命令名字 默认是文件夹名
	Cmd string
	// 加入子命令配置文件名
	Register string
	// 插件目录
	Dir string
}

// 命令类型
type CommandType int

const (
	// 安装类型
	Add CommandType = iota
	// 初始化资源
	Init
	// 更新类型
	Upgrade
	// 卸载类型
	Remove
	// 启动服务类型
	Start
	// 停止服务类型
	Stop
	// 独有的类型
	Exclusive
	// 工具类软件 不能启动服务
	Kit
	// 服务性软件
	Service
	// 将来定义类型
	TODO
)

// 统一日志级别
type LogLevel int

const (
	Silent LogLevel = iota + 1
	Error
	Warn
	Info
)

func (l LogLevel) String() string {
	switch l {
	case Silent:
		return "silent"
	case Error:
		return "error"
	case Warn:
		return "warn"
	default:
		return "info"
	}
}

//获取日志级别
func GetLogLevel(s string) LogLevel {
	switch strings.ToLower(s) {
	case "silent":
		return Silent
	case "error":
		return Error
	case "warn":
		return Warn
	default:
		return Info
	}
}

//适配数据库日志级别
func GetDBLogLevel(s string) logger.LogLevel {
	switch strings.ToLower(s) {
	case "silent":
		return logger.Silent
	case "error":
		return logger.Error
	case "warn":
		return logger.Warn
	default:
		return logger.Info
	}
}

//获取操作命令类型
func GetCommandType(ct string) CommandType {
	if res, ok := cmdTypes[ct]; ok {
		return res
	}
	panic("无效的命令类型:" + ct)
}

//输出操作命令类型字符串
func (ct CommandType) String() string {
	for k, tmp := range cmdTypes {
		if tmp == ct {
			return k
		}
	}
	panic("无效的命令类型")
}

// 反序列化配置文件属性
func UnmarshalConfig(ptr interface{}, keys ...string) {
	if err := viper.UnmarshalKey(getConfigKey(keys), ptr); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

// 不存在cmdName属性时才生效 不会覆盖已有配置
func SetDefaultConfig(defData interface{}, keys ...string) {
	viper.SetDefault(getConfigKey(keys), defData)
}

func BindConfig(cfgFile string, ptr interface{}, keys ...string) error {
	v, ok := cacheViper[cfgFile]
	if !ok {
		v = viper.New()
		v.SetConfigFile(cfgFile)
		v.SetConfigType(cfgFile[strings.LastIndex(cfgFile, ".")+1:])
		if err := v.ReadInConfig(); err != nil {
			return err
		}
		cacheViper[cfgFile] = v
	}

	tmpKey := getConfigKey(keys)
	return v.UnmarshalKey(tmpKey, ptr)
}

func getConfigKey(keys []string) string {
	return strings.Join(keys, ".")
}

func GetConfigKey(keys ...string) string {
	return getConfigKey(keys)
}

func GetAllSettings() map[string]interface{} {
	return viper.AllSettings()
}
